﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web.Services.Protocols;

namespace PTA.Net
{
    /// <summary>
    /// SOAP请求信息和返回信息拦截类
    /// </summary>
    public class SoapCatch : System.Web.Services.Protocols.SoapExtension
    {
        /// <summary>
        /// 默认的Soap信息记录器
        /// </summary>
        private static LogAdapter logger = LogAdapter.GetLogger("SOAP");

        private static ISoapCatchFilter writter = new DefaultSoapCatchFilter();
        /// <summary>
        /// 获取或设置Soap信息记录器,如果不设置,则使用默认的记录器
        /// </summary>
        public static ISoapCatchFilter CatchFilter
        {
            get
            {
                return writter;
            }
            set
            {
                writter = value;
            }
        }

        private Stream ms;//可以传给低扩展的流
        private Stream soapStream;//优先级高的扩展传递进来的流

        /// <summary>
        /// 获取当前请求的流
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public override Stream ChainStream(Stream stream)
        {
            soapStream = stream;
            return ms = new MemoryStream();
        }

        /// <summary>
        /// 处理请求信息
        /// </summary>
        /// <param name="message"></param>
        public override void ProcessMessage(SoapMessage message)
        {
            switch (message.Stage)
            {
                case SoapMessageStage.AfterSerialize:
                    WriteOutput(message);
                    break;
                case SoapMessageStage.BeforeDeserialize:
                    WriteInput(message);
                    break;
            }
        }

        private void WriteOutput(SoapMessage message)
        {
            //这里是准备向网络传输的，即客户端发出请求或服务器端发出响应时发生的。
            //在这两个阶段ChainStream 调用顺序和ProcessMessage相反，所以可以肯定_newStream 
            //一定不为空，而_oldStream 为空。
            using (ms as IDisposable)
            {
                ms.Seek(0, SeekOrigin.Begin);
                StreamReader sr = new StreamReader(ms, System.Text.Encoding.UTF8);
                string xml = string.Empty;
                if ((message is SoapClientMessage) ? writter.ClientProcessRequest(message, sr.ReadToEnd(), out xml) : writter.ServerProcessResponse(message, sr.ReadToEnd(), out xml))
                {
                    var resultbytes = System.Text.Encoding.UTF8.GetBytes(xml);
                    soapStream.Write(resultbytes, 0, resultbytes.Length);
                }
                else
                {
                    ms.Seek(0, SeekOrigin.Begin);
                    ms.CopyTo(soapStream);
                }
            }
        }

        private void WriteInput(SoapMessage message)
        {
            soapStream.Seek(0, SeekOrigin.Begin);
            using (soapStream as IDisposable)
            using (StreamReader sr = new StreamReader(soapStream, System.Text.Encoding.UTF8))
            {
                string xml = string.Empty;
                if ((message is SoapClientMessage) ? writter.ClientProcessResponse(message, sr.ReadToEnd(), out xml) : writter.ServerProcessRequest(message, sr.ReadToEnd(), out xml))
                {
                    var resultbytes = System.Text.Encoding.UTF8.GetBytes(xml);
                    ms.Write(resultbytes, 0, resultbytes.Length);
                }
                else
                {
                    soapStream.Seek(0, SeekOrigin.Begin);
                    soapStream.CopyTo(ms);
                }
                ms.Seek(0, SeekOrigin.Begin);
            }
        }

        /// <summary>
        /// 获取初始化器
        /// </summary>
        /// <param name="serviceType">类型</param>
        /// <returns></returns>
        public override object GetInitializer(Type serviceType)
        {
            return null;
        }

        /// <summary>
        /// 获取初始化器
        /// </summary>
        /// <param name="methodInfo">类型</param>
        /// <param name="attribute">属性</param>
        /// <returns></returns>
        public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
        {
            return null;
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="initializer">初始化器</param>
        public override void Initialize(object initializer)
        {
        }
    }

    /// <summary>
    /// Soap信息过滤器,用户可根据参数message的详细信息来进行Soap包的过滤和日志和记录,使用了这个原来的功能会失效
    /// <para>
    /// 使用方法:SoapCatch.CatchFilter = new SoapCatchFilter();
    /// </para>
    /// </summary>
    public interface ISoapCatchFilter
    {

        /// <summary>
        /// 客户端处理请求的数据
        /// </summary>
        /// <param name="message">Soap包信息</param>
        /// <param name="xml">客户端请求的数据流中的XML数据</param>
        /// <param name="newxml">如果修改了客户端输入的数据，将结果存入这个参数</param>
        /// <returns>返回一个值，指示是否修改了客户端请求的数据</returns>
        bool ClientProcessRequest(SoapMessage message, string xml, out string newxml);

        /// <summary>
        /// 客户端处理响应的数据
        /// </summary>
        /// <param name="message">Soap包信息</param>
        /// <param name="xml">响应给客户端的数据流中的XML数据</param>
        /// <param name="newxml">如果修改了响应给客户端的数据，将结果存入这个参数</param>
        /// <returns>返回一个值，指示是否修改了响应给客户端的数据</returns>
        bool ClientProcessResponse(SoapMessage message, string xml, out string newxml);

        /// <summary>
        /// 服务端处理来自客户端请求的数据
        /// </summary>
        /// <param name="message">Soap包信息</param>
        /// <param name="xml">Soap包内容</param>
        /// <param name="newxml">如果修改了客户端输入的数据，将结果存入这个参数</param>
        /// <returns>数据处理后的结果</returns>
        bool ServerProcessRequest(SoapMessage message, string xml, out string newxml);

        /// <summary>
        /// 服务端处理返回给客户端的数据
        /// </summary>
        /// <param name="message">Soap包信息</param>
        /// <param name="xml">Soap包内容</param>
        /// <param name="newxml">如果修改了响应给客户端的数据，将结果存入这个参数</param>
        /// <returns>数据处理后的结果</returns>
        bool ServerProcessResponse(SoapMessage message, string xml, out string newxml);
    }

    /// <summary>
    /// 一个默认的Soap记录过虑器
    /// </summary>
    internal class DefaultSoapCatchFilter : ISoapCatchFilter
    {
        #region SoapCatchFilter Members

        /// <summary>
        /// 写请求数据
        /// </summary>
        /// <param name="message">Soap包信息</param>
        /// <param name="xml">Soap包内容</param>
        public virtual bool ClientProcessRequest(SoapMessage message, string xml, out string newxml)
        {
            WriteSoapMessage(message, xml, "REQ ");
            newxml = string.Empty;
            return false;
        }

        /// <summary>
        /// 写响应数据
        /// </summary>
        /// <param name="message">Soap包信息</param>
        /// <param name="xml">Soap包内容</param>
        public virtual bool ClientProcessResponse(SoapMessage message, string xml, out string newxml)
        {
            WriteSoapMessage(message, xml, "RES ");
            newxml = string.Empty;
            return false;
        }

        /// <summary>
        /// 写请求数据
        /// </summary>
        /// <param name="message">Soap包信息</param>
        /// <param name="xml">Soap包内容</param>
        public virtual bool ServerProcessRequest(SoapMessage message, string xml, out string newxml)
        {
            WriteSoapMessage(message, xml, "REQ ", System.Web.HttpContext.Current.Request.Headers["SOAPAction"]);
            newxml = string.Empty;
            return false;
        }

        /// <summary>
        /// 写响应数据
        /// </summary>
        /// <param name="message">Soap包信息</param>
        /// <param name="xml">Soap包内容</param>
        public virtual bool ServerProcessResponse(SoapMessage message, string xml, out string newxml)
        {
            WriteSoapMessage(message, xml, "RES ");
            newxml = string.Empty;
            return false;
        }

        private void WriteSoapMessage(SoapMessage message, string xml, string direction, string action = null)
        {
            action = action ?? message.Action;
            string method = action.Substring(action.LastIndexOf("/") + 1);
            LogAdapter.GetLogger(string.Format("SOAP{0}{1}", Path.DirectorySeparatorChar, method)).Info("{1}:{0}URL :{2}{0}ACT :{3}{0}{4}", false, LogFileSpan.Day, Environment.NewLine, direction, message.Url, action, Convert.FormatXml(xml));
        }
        #endregion
    }
}
